package org.tony.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.beanutils.BeanUtils;
import org.tony.entity.DbEntry;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * 数据源辅助类
 * @author tony
 *
 */
public class XmlDataSourceHelper {

	private static final String DATA_SOURCE_PATH = "configs/datasource.xml";
	private static final String RELATION_PATH = "configs/config-xmlentry.properties";
	private static final Properties PRO = new Properties();
	private static final ConcurrentMap<String,DbEntry> entries = new ConcurrentHashMap<String,DbEntry>();
	
	private boolean initial =false ;
	private static XmlDataSourceHelper instance = new XmlDataSourceHelper();

	private XmlDataSourceHelper() {
	
	}
	
	/**
	 * 初始化
	 */
	public  synchronized void init() {
		if(!initial) {
			loadXmlEntryProperties();
			initDataSourceBean() ;
			initial = true ;
		}
	}
	/**
	 * 加载xml文档中的 节点名称与实体的属性名称之间的对应关系文件
	 */
	private synchronized void loadXmlEntryProperties() {
		InputStream fis = Thread.currentThread().getContextClassLoader()
				.getResourceAsStream(RELATION_PATH);
		try {
			PRO.load(fis);
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			try {
				fis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 解析数据源对象
	 */
	private synchronized void initDataSourceBean() {
		List<Map<String,String>> beans = parseXmlSource(DATA_SOURCE_PATH);
		if(beans!=null && beans.size()>0) {
			for(Map<String,String> node:beans) {
				DbEntry db = new DbEntry() ;
				for(Iterator<Map.Entry<String, String>> iter = node.entrySet().iterator();iter.hasNext();) {
					Map.Entry<String, String> cur = iter.next() ;
					try {
						BeanUtils.setProperty(db, PRO.get(cur.getKey()).toString(), cur.getValue()) ;
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					} catch (InvocationTargetException e) {
						e.printStackTrace();
					}
				}
				entries.put(db.getId(), db);
			}
		}
	}
	
	/**
	 * 解析xml文件
	 * @param xmlPath
	 * @return
	 */
	private synchronized List<Map<String,String>> parseXmlSource(String xmlPath) {
		try {
			List<Map<String,String>> nodes = new ArrayList<Map<String,String>>();
			Document doc = getDocuemnt(xmlPath);
			Element root = doc.getDocumentElement();
			NodeList firstLevelNodes = root.getChildNodes();
			if (firstLevelNodes != null) {
				for (int i = 0; i < firstLevelNodes.getLength(); i++) {
					Node dataSource = firstLevelNodes.item(i);
					if (dataSource.getNodeType() == Node.ELEMENT_NODE) {
						NodeList dataSourceInfo = dataSource.getChildNodes();
						if (dataSourceInfo != null
								&& dataSourceInfo.getLength() > 0) {
							Map<String,String> curNodes  = new HashMap<String, String>();
							for (int j = 0; j < dataSourceInfo.getLength(); j++) {
								Node cur = dataSourceInfo.item(j);
								if (cur.getNodeType() == Node.ELEMENT_NODE) {
									String curNodeValue = cur.getFirstChild()
											.getNodeValue();
									curNodes.put(cur.getNodeName(), curNodeValue) ;
								}
							}
							nodes.add(curNodes);
						}
					}
				}
			}
			return nodes ;
		} catch (Exception e) {
			e.printStackTrace();
			return null ;
		}
	}
	
	/**
	 * 根据数据源实体，生成Xml node
	 * @param entry
	 * @param doc
	 * @return
	 */
	private Node generateNode(DbEntry entry,Document doc) {
		Node node = doc.createElement("DatabaseInfo") ;
		
		Node id = doc.createElement("ID") ;
		id.appendChild(doc.createTextNode(entry.getId()));
		
		
		Node databaseType = doc.createElement("DatabaseType") ;
		databaseType.appendChild(doc.createTextNode(entry.getDatabaseType()));
		
		Node databaseName = doc.createElement("DatabaseName") ;
		databaseName.appendChild( doc.createTextNode(entry.getDatabaseName()));
		
		Node databaseIp = doc.createElement("DatabaseIp")  ;
		databaseIp.appendChild(doc.createTextNode(entry.getDataSourceIp()));
		
		Node port = doc.createElement("Port")  ;
		port.appendChild(doc.createTextNode(String.valueOf(entry.getPort())));
		
		Node driverClass =  doc.createElement("DriverClass") ;
		driverClass.appendChild(doc.createTextNode(entry.getDriverClass()));
		
		Node userName = doc.createElement("UserName") ;
		userName.appendChild(doc.createTextNode(entry.getUsername()) );
		
		Node password = doc.createElement("Password") ;
		password.appendChild(doc.createTextNode(entry.getPassword()) ) ;
		
		
		Node zhuceAdmin = doc.createElement("ZhuceAdmin")  ;
		zhuceAdmin.appendChild( doc.createTextNode(entry.getRegeUsername()) );
		
		Node zhuceDate = doc.createElement("ZhuceDate") ;
		zhuceDate.appendChild( doc.createTextNode(entry.getRegDate()) );
		
		node.appendChild(id);
		node.appendChild(databaseType);
		node.appendChild(databaseName);
		node.appendChild(databaseIp);
		node.appendChild(port);
		node.appendChild(driverClass);
		node.appendChild(userName);
		node.appendChild(password);
		node.appendChild(zhuceAdmin);
		node.appendChild(zhuceDate);
		
		return node;
	}
	
	public synchronized DbEntry  findDbEntryById(String id) {
		return entries.get(id);
	}
	
	public static Document getDocuemnt(String xmlFilePath) throws Exception{
		DocumentBuilderFactory docFac = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = docFac.newDocumentBuilder();
		URI uri = Thread.currentThread().getContextClassLoader().getResource(xmlFilePath).toURI();
	//	System.out.println(uri);
		InputStream fis = new FileInputStream(new File(uri));
		Document doc = builder.parse(fis);
		fis.close();
		return doc;
	}

	/**
	 * 写xml文档
	 * @param entry
	 * @return
	 */
	public synchronized boolean writeXmlEntry(DbEntry entry) {

		try {
			Document doc = getDocuemnt(DATA_SOURCE_PATH);
			Element root = doc.getDocumentElement();
			root.appendChild(generateNode(entry, doc));
			output(doc,DATA_SOURCE_PATH);
			entries.put(entry.getId(),entry);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false ;
		}
	}
	
	public Collection<DbEntry> getDbEntryList() {
		return entries.values();
	}

	/**
	 * 写xml文件
	 * @param doc
	 * @param fileString
	 * @throws Exception
	 */
	public static  void  output(Document doc,String fileString) throws Exception {
		TransformerFactory tf = TransformerFactory.newInstance();
		Transformer transformer = tf.newTransformer();
		DOMSource source = new DOMSource(doc) ;
		URI uri = Thread.currentThread().getContextClassLoader().getResource(fileString).toURI();
		File file = new File(uri) ;
		StreamResult result = new StreamResult(file);
		System.out.println(uri.getPath());
		transformer.transform(source, result);
	}
	
	/**
	 * 删除数据源
	 * @param id
	 * @return
	 */
	public synchronized boolean deleteXmlEntry(String id) {
		try {
			Document doc =getDocuemnt(DATA_SOURCE_PATH) ;
			Element root = doc.getDocumentElement();
			NodeList cur = queryNodes("/datasources/DatabaseInfo[ID=\""+id+"\"]", root);
			if(cur!=null) {
				root.removeChild(cur.item(0));
				output(doc,DATA_SOURCE_PATH);
				deleteFromEntries(id);
				return true;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false ;
	}
	
	/**
	 * 利用Xpath查找节点
	 * @param express
	 * @param source
	 * @return
	 */
	public static NodeList queryNodes(String express,Object source) {
		NodeList result=null;
        XPathFactory xpathFactory=XPathFactory.newInstance();
        XPath xpath=xpathFactory.newXPath();
        try {
            result= (NodeList)xpath.evaluate(express, source, XPathConstants.NODESET);
        } catch (XPathExpressionException e) {
            e.printStackTrace();
        }
        
        return result;
	}
	
	private void deleteFromEntries(String id) {
		entries.remove(id);
	}
	
	public static XmlDataSourceHelper getInstance() {
		return instance;
	}


}
